home *** CD-ROM | disk | FTP | other *** search
/ Apple WWDC 1996 / WWDC96_1996 (CD).toast / Technology Materials / MacApp Release 10 / MacApp Release 10 - HD Ready / Libraries / Framework / Includes / UBusyCursor.h < prev    next >
Encoding:
Text File  |  1996-04-03  |  13.7 KB  |  321 lines  |  [TEXT/MPS ]

  1. // UBusyCursor.h
  2. // Copyright © 1984-96 by Apple Computer, Inc. All rights reserved.
  3.  
  4. #ifndef __UBUSYCURSOR__
  5. #define __UBUSYCURSOR__
  6.  
  7. //----------------------------------------------------------------------------------------
  8. // THEORY OF OPERATION
  9. //
  10. // The UBusyCursor unit implements a mechanism for automatically setting the cursor to the
  11. // busy cursor when the application is "busy." An application is considered "busy" if it
  12. // hasn't called GetNextEvent or EventAvail for a given number of ticks, where 60 ticks
  13. // equals one second.The default time is defined by kBusyDelay, which is 120 ticks or 2
  14. // seconds--it can be changed by calling BusyDelay. After this period of time has elapsed
  15. // with no call to GetNextEvent or EventAvail, the cursor is changed to the busy cursor.On
  16. // the next call to GetNextEvent or EventAvail, the cursor is restored to its state before
  17. // it was changed to the busy cursor.
  18. //  
  19. // Changing the cursor to the busy cursor is done by the VBL task ABusyTask. ABusyTask's
  20. // execution frequency is set to kBusyDelay, or by calling BusyDelay. When ABusyTask is
  21. // executed it sets the cursor to the busy cursor and resets ABusyTask's vblCount. The
  22. // traps InitCursor, SetCursor and SetCCursor are patched so that they "remember" the
  23. // cursor being set before executing the trap.This will allow us to restore the cursor
  24. // after it has been changed to the busy cursor.
  25. //
  26. // The EventAvail and GetNextEvent traps are patched such that, before executing the trap,
  27. // ABusyTask's vblCount is reset, and if the busy cursor is displayed, then the cursor is
  28. // restored to the last cursor set by SetCursor or SetCCursor.
  29. //
  30. // Manual vs. Automatic Animation
  31. //
  32. // Now, there are two philosophies as to how to make a busy cursor animate: automatic and
  33. // manual. The automatic technique requires no intervention by the application, and is
  34. // essentially an extension of the current MacApp busy cursor philosophy. One way of
  35. // implementing automated animation is by installing a VBL task which changes the cursor
  36. // at appropriate time intervals, without the applications knowledge. The advantage of
  37. // this approach, of course, is that you dont have to do anything in your application to
  38. // make it work, plus you get a cursor that animates in a smooth manner. The drawback is
  39. // the possibility of a run away cursor. By using a VBL task the watch may spin even
  40. // though the application has locked up, say in an endless loop, and isnt actually doing
  41. // anything. This is because the VBL task has no idea what the application is doing. This
  42. // happened to me (Curt Bianchi) once with a well-known word processor. I tried to save a
  43. // file and the word processor locked up for hours, but the watch kept spinning.
  44. // Eventually I gave up and pressed the program interrupt switch on the side of the
  45. // computer. Since I didnt have a debugger installed, the bomb alert came up, and all the
  46. // while the hands kept spinning, oblivious to what was happening!
  47. //
  48. // With the manual technique the application tells the cursor when to animate. This is
  49. // done by sprinkling cursor animation calls throughout your code, which can be a real
  50. // pain. You also wind up with jumpy or irregular animation. However, it does prevent the
  51. // run away cursor problem. Both the Finder (6.0) and MPW (3.2) use this technique.
  52. //
  53. // It turns out that theres a simple way to combine the advantages of both approaches,
  54. // which Ill use here. That is, you can get smooth animation and avoid the run away cursor
  55. // problem. Its done by animating the cursor as long as the application lets us know its
  56. // still working. If the application fails to check in, then the animation times out. You
  57. // can choose between automatic and manual animation by setting the length of time before
  58. // time out occurs. A very large time out value, say LONG_MAX which for our purposes is
  59. // infinity, means the application never has to check in and we get automatic cursor
  60. // animation. A small time out value, say a few minutes, forces the application to check
  61. // in from time to time, or the cursor quits animating after the period of time has
  62. // elapsed.
  63. //  
  64. // To implement this scheme, we need a way for the application to check in, and a way for
  65. // the application to set the period of time after which the animation times out. To set
  66. // the period of time well add the routine SetBusyTimeout, which accepts a value measured
  67. // in ticks, or sixtieths (1/ 60) of a second. For example, five minutes is 18000 ticks: 5
  68. // * 60 * 60 = 18000. Well also implement the routine BusyAnimate, which the application
  69. // calls to notify us that its still working. Provided BusyAnimate is called more often
  70. // than the time out value, the cursor continues to animate smoothly. So, if you pass
  71. // 18000 (5 minutes) to SetBusyTimeout, the busy cursor will continue to animate as long
  72. // as BusyAnimate is called at least every five minutes, or until the application attempts
  73. // to handle another event.
  74. //----------------------------------------------------------------------------------------
  75.   
  76. // MacApp
  77.  
  78. #ifndef __UOBJECT__
  79. #include "UObject.h"
  80. #endif
  81.  
  82. // Toolbox
  83.  
  84. #ifndef __QUICKDRAW__
  85. #include <Quickdraw.h>
  86. #endif
  87.  
  88. #ifndef __RETRACE__
  89. #include <Retrace.h>
  90. #endif
  91.  
  92. // ANSI
  93.  
  94. #ifndef __LIMITS__
  95. #include <limits.h>
  96. #endif
  97.  
  98.  
  99. //----------------------------------------------------------------------------------------
  100. // Constants
  101. //----------------------------------------------------------------------------------------
  102.  
  103. const short kBusyDelay = 2 * 60;                // default # of 1/ 60 sec. ticks before
  104.                                                 // cursor changes to a busy cursor
  105.                                                   
  106. const short kAnimateDelay = 10;                    // Animate every 1/ 6 of a second.
  107.  
  108. const long kTicksBeforeTimeout = LONG_MAX;        // Default is to never time out
  109.  
  110.  
  111. //----------------------------------------------------------------------------------------
  112. // These types are used to represent the acur resources. The 'cursors' field will actually
  113. // index into a variable number of cursors. We need one as a place holder.
  114. //----------------------------------------------------------------------------------------
  115.  
  116. #if PRAGMA_ALIGN_SUPPORTED
  117. #pragma options align=mac68k
  118. #endif
  119. struct AcurRsrcRecord
  120. {
  121.     short noOfCursors;
  122.     ResNumber whichCursor;
  123.     
  124.     union
  125.     {
  126.         struct
  127.         {
  128.             ResNumber rsrcId;
  129.             short filler;
  130.         } Disk;
  131.             // The cursor resource is still on disk, and needs to be read in.
  132.             
  133.         struct
  134.         {
  135.             CursHandle h;
  136.         } Memory;
  137.             // The cursor resource is currently in memory.
  138.             
  139.     } cursors[1];
  140. };
  141. #if PRAGMA_ALIGN_SUPPORTED
  142. #pragma options align=reset
  143. #endif
  144.  
  145. typedef AcurRsrcRecord* AcurRsrcPtr;
  146. typedef AcurRsrcPtr* AcurRsrcHandle;
  147.  
  148. class TBusyCursor;
  149.  
  150. //----------------------------------------------------------------------------------------
  151.  
  152. struct QElemWithBusyCursor
  153. {
  154.     VBLTask q;                                    // vbl queue element for changing the cursor
  155.     TBusyCursor* fBusyCursor;
  156.  
  157.     QElemWithBusyCursor();
  158. };
  159.  
  160. inline QElemWithBusyCursor::QElemWithBusyCursor()
  161. {
  162.     fBusyCursor = NULL;
  163. }
  164.  
  165. //----------------------------------------------------------------------------------------
  166. // TBusyCursor
  167. //----------------------------------------------------------------------------------------
  168.  
  169. class TBusyCursor : public TObject
  170. {
  171.     MA_DECLARE_CLASS;
  172.     
  173. public:
  174.  
  175.     TBusyCursor();
  176.         // Empty constructor to satisfy compiler.
  177.         
  178.     void IBusyCursor();
  179.         // IBusyCursor installs the busy cursor mechanism. Typically this is called once
  180.         // during program initialization. It installs the VBL task and patches the traps.
  181.  
  182.     virtual ~TBusyCursor();
  183.         // Free uninstalls the busy cursor mechanism. Typically this is called once during
  184.         // program termination.It removes the VBL task and unpatches the patched traps.
  185.  
  186.     virtual AcurRsrcHandle InitializeAnimatedCursor(ResNumber acurRsrcId);
  187.         // Loads the acur resource whose id is acurRsrcId, and then loads the CURS
  188.         // resources referred to by the acur resource.All of these resources must be
  189.         // locked in memory since they are accessed at VBL interrupt time.We also detach
  190.         // them from the resource map to prevent the Resource Mgr from unlocking them out
  191.         // from under us.
  192.  
  193.     virtual Boolean Activate(Boolean entering);
  194.         // Call BusyActivate if you want to activate or deactivate the busy cursor
  195.         // mechanism. This is call by MacApp when losing/ gaining control to a desk
  196.         // accessory or another application.
  197.  
  198.     virtual Boolean InControl(Boolean entering, Boolean reset);
  199.         // Allows shutting off control, checking events via ENE/GNE/WNE and
  200.         // restoring control all without disturbing (resetting) a busy cursor already
  201.         // being shown.
  202.         
  203.     virtual void SetDelay(short newDelay);
  204.         // Call SetDelay if you want to change the busy cursor delay. newDelay should be in
  205.         // 1/60 seconds; a value <= 0 means don't change the (SetDelay respects the state flags
  206.         // in the cursor info record (ie., changeToBusy and inControl.)
  207.  
  208.     virtual void ForceBusy();
  209.         // Puts up the busy cursor immediately.
  210.  
  211.     virtual void Reset(short delayTicks);
  212.         // Call Reset if you want to change the cursor back to an arrow and reset the time
  213.         // before changing back to a busy cursor.This is not usually called directly by
  214.         // the application.Instead, it is called each time GetNextEvent and EventAvail is
  215.         // called, by patches installed by IBusyCursor.
  216.  
  217.     virtual void TurnOff();
  218.         // This is called from InitMacAppCursor, SetMacAppCursor and SetCMacAppCursor. It
  219.         // sets pCursorInfo fields to indicate that the cursor is not the busy cursor.
  220.  
  221.     virtual void Animate();
  222.         // call this to prevent the animation from timing out.
  223.  
  224.     virtual void SetTimeout(long ticksBeforeTimeout);
  225.         // call this to set the length of time before animation times out.
  226.  
  227.     virtual Boolean KeepCursorBusy(Boolean keepItSpinning);
  228.         // set this if you want to call any of the patched traps without resetting the
  229.         // cursor
  230.  
  231.     virtual void SetAnimatedCursor(ResNumber itsACUR, short animateDelay);
  232.         // Call this to set the cursor to a new animation sequence
  233.  
  234.     virtual void ReleaseAnimatedCursor();
  235.         // Call this to release (dispose) the current animation sequence
  236.  
  237.     virtual void SetMacAppCursor(const Cursor* theCursor);
  238.     // Remembers the cursor being set
  239.  
  240.     virtual void SetCMacAppCursor(CCrsrHandle theCCursor);
  241.     // Remembers the cursor being set
  242.  
  243.     virtual void BusyTask();
  244.     // Called from VBL to perform the busy task
  245.  
  246.     //----------------------------------------------------------------------------------------
  247.     // static member functions
  248.     //----------------------------------------------------------------------------------------
  249. public:
  250.  
  251.     static void InitUBusyCursor();
  252.         // Inits the unit. Creates the global gBusyCursor.
  253.     
  254.     static void TerminateUBusyCursor();
  255.         // Terminates the unit
  256.     
  257.     static CursHandle NextAnimatedCursor(AcurRsrcHandle acurRsrc);
  258.         // This routine will return the next cursor from the animation list.
  259.     
  260.     static void ResetBusyCursor();
  261.  
  262. protected:
  263.     static pascal void SetCMacAppCursorPatch(CCrsrHandle theCCursor);
  264.     static pascal void SetMacAppCursorPatch(const Cursor* theCursor);
  265.     static pascal void InitMacAppCursor();
  266.  
  267.     //----------------------------------------------------------------------------------------
  268.     // data members
  269.     //----------------------------------------------------------------------------------------
  270. public:
  271.     QElemWithBusyCursor fQElemWithBusyCursor;    // vbl queue elem. for changing the cursor
  272.     Cursor fOrigCursor;                            // Cursor at the time the busy cursor was
  273.                                                 // put up, if not in color
  274.     Cursor fCurrentCursor;                        // the current display cursor
  275.     AcurRsrcHandle fCursorState;                // the acur resource
  276.     CCrsrHandle fOrigCCursor;                    // Cursor at the time the busy cursor was
  277.                                                 // put up, if in color
  278.     long fTimeoutCount;                            // spins before animation times out
  279.     long fSpinCount;                            // spins since last BusyAnimate call 
  280.  
  281.     long fA5;                                    // The value of A5 will be stored here to
  282.                                                 // be available at VBL time
  283.     short fBusyDelay;                            // time in 1/ 60 second before cursor
  284.                                                 // changes to watch
  285.     short fAnimateDelay;                        // ticks before we spin the cursor
  286.     Boolean fInColor;                            // Is the saved cursor in color?
  287.     Boolean fInControl;                            // managed by MacApp; true iff MacApp is
  288.                                                 // in control(); if false we don't change
  289.                                                 // the cursor at all
  290.     Boolean fChangeToBusy;                        // if true, we automagically switch to the
  291.                                                 // busy cursor in the VBL task and switch
  292.                                                 // to fOrigCursor on a call to GetNextEvent
  293.                                                 // or EventAvail(); applications can
  294.                                                 // changed this as necessary
  295.     Boolean fIgnoreReset;                        // cursor has been actively put into a
  296.                                                 // state that will ignore resets.
  297.     Boolean fBusyOn;                            // true if the busy cursor on
  298.  
  299.     //----------------------------------------------------------------------------------------
  300.     // static data members
  301.     //----------------------------------------------------------------------------------------
  302. public:
  303.  
  304.     static TBusyCursor* fgBusyCursor;            // THE busycursor handler.
  305.  
  306. protected:
  307.     static Boolean pBusyCursorVBLInstalled;        // used for proper failure handling
  308. };
  309.  
  310.  
  311. //----------------------------------------------------------------------------------------
  312. // External declarations for global variables and funcitons.
  313. //----------------------------------------------------------------------------------------
  314. typedef TBusyCursor *TBusyCursorPtr;
  315.  
  316. extern TBusyCursorPtr& gBusyCursor;
  317.     // We'll leave this a reference which is initialized to refer to the static member
  318.     // for compatibility.
  319.  
  320. #endif
  321.